﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tilver.Common;
using System.Reflection;
using System.Data;

namespace Tilver.Data
{

    internal class ReflectionRowMapper
    {
        private static readonly Dictionary<Type, RowMapping> _mapCache = new Dictionary<Type, RowMapping>();
        private static object _syncLock = new object();
        public static Func<IDataReader, T> GetRowMapper<T>()
        {
            Func<IDataReader, T> ret = null;
            Type type = typeof(T);
            if (!_mapCache.ContainsKey(type))
            {
                lock (_syncLock)
                {
                    if (!_mapCache.ContainsKey(type))
                    {
                        RowMapping mapper = new RowMapping(type);
                        _mapCache.Add(type, mapper);
                        ret = new Func<IDataReader, T>(mapper.Build<T>);
                    }
                }
            }
            else
            {
                ret = new Func<IDataReader, T>(_mapCache[type].Build<T>);
            }
            return ret;
        }

    }
    internal class RowMapping
    {
        private Type _type;
        private BuildHandler _buildHandler;
        private readonly Dictionary<string, SetHandler> _columnMapper = new Dictionary<string, SetHandler>();
        public RowMapping(Type t)
        {
            _type = t;
            _buildHandler = DynamicMethodCompiler.CreateBuildHandler(t);
            PropertyInfo[] properties = (from p in _type.GetProperties()
                                         where p.IsDefined(typeof(ColumnMapperAttribute), true)
                                         select p).ToArray<PropertyInfo>();

            foreach (PropertyInfo property in properties)
            {
                ColumnMapperAttribute attr = (ColumnMapperAttribute)Attribute.GetCustomAttribute(property, typeof(ColumnMapperAttribute));
                string key = attr.ColumnName;
                SetHandler handler = DynamicMethodCompiler.CreateSetHandler(_type, property);
                _columnMapper.Add(key, handler);
            }
        }
        private List<Action<object, SetHandler>> _propertyMapper = new List<Action<object, SetHandler>>();
        public T Build<T>(IDataReader reader)
        {
            object ret = _buildHandler();
            foreach (KeyValuePair<string, SetHandler> item in _columnMapper)
            {
                item.Value.Invoke(ret, reader[item.Key]);
            }
            return (T)ret;
        }
    }
}
